package testApplets;

import java.awt.geom.Line2D;
import java.io.BufferedOutputStream;
import java.io.DataOutputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Vector;

import controlP5.ControlP5;
import controlP5.RadioButton;

import marchingCubes.DualCubeEngine;
import marchingCubes.IsMCVertex;
import marchingCubes.MCube;
import marchingCubes.MCubeEngine;
import marchingCubes.MCubeEngineLowMemory;
import meshToSolid.ContainerGrid;
import meshToSolid.STLWriter;
import meshToSolid.ContainerGrid.Container;

import peasy.PeasyCam;
import processing.core.PApplet;
import processing.core.PGraphics3D;
import processing.core.PMatrix3D;
import processing.core.PVector;

import simpleGeom.BasicMesh;
import simpleGeom.Bounds3D;
import simpleGeom.Box3D;
import simpleGeom.DepthAnalyser3D;
import simpleGeom.Face3D;
import simpleGeom.HasBounds3D;
import simpleGeom.HasPos3D;
import simpleGeom.Line3D;
import simpleGeom.Mesh;
import simpleGeom.SimpleTriangle;

import simpleGeom.Node;
import simpleGeom.SimpleObjLoaderStream;
import simpleGeom.Triangle3DUVN;
import simpleGeom.TrianglePreCalc;
import simpleGeom.Vec3D;

public class MeshToSolidColumn extends PApplet {

	Bounds3D b3d = new Bounds3D();
	Bounds3D realBounds = new Bounds3D();
	BasicMesh dualMesh;
	BasicMesh mesh = new BasicMesh();
	Vector<SimpleTriangle> borderFaces = new Vector<SimpleTriangle>();
	Vector<SimpleTriangle> meFaces;
	Cell[][][] grid;

	float sX;
	float sY;
	float sZ;
	int nX;
	int nY;
	int nZ;

	String folderPath = "/Users/caad/Desktop/Archive/";
	String pathObjIn = folderPath + "columnM1revisit.obj";
	String pathDistOut = pathObjIn + ".txt";
	String pathObjOut = "";

	// gui
	PeasyCam cam;
	ControlP5 cp5;
	int guiWidth = 200;
	boolean flagMarching = false;
	boolean flagDistance = false;
	boolean flagLoadDistance = false;
	boolean flagResetPreview;
	boolean flagResetFine;
	boolean flagRefresh;
	boolean flagSave;

	// config
	float dimHigh = 0.00029f;// 0.0034//0.003//0.007
	float dim = 0.03f;
	float goalThick = 0.03f;
	float c = dimHigh * 0.9f / ((goalThick - dimHigh * 0.9f) * 0.5f);
	float dimFac = 0.15f;// thickness inside
	float dimLow = 0.006f;// 0.1
	float dimHull = dim * 0.5f;// 0.003//0.007
	float iso = 0.01f;// 0.005//0.006

	public float minX = 0.4f;// 26
	public float maxX = 0;// 5
	public float minY = 0.4f;// 20
	public float maxY = 0.45f;// 72
	public float minZ = 0.0f;// 28
	public float maxZ = 0.0f;// 28

	float goalX = 1.7f;
	float goalY = 3.5f;
	float goalZ = 1.7f;

	public void setup() {
		size(1300, 750, P3D);
		b3d = new Bounds3D();
		realBounds = new Bounds3D();

		loadData();
		setBounds();
		println("exportBds: " + b3d);
		resetPreview();
		findBorders();
		createGUI();
		cam = new PeasyCam(this, 400);
	}

	public void createGUI() {
		cp5 = new ControlP5(this);
		cp5.addSlider("minX").setPosition(20, 20).setRange(0, 1)
				.setSize(guiWidth, 20);
		;
		cp5.addSlider("maxX").setPosition(20, 50).setRange(0, 1)
				.setSize(guiWidth, 20);
		;
		cp5.addSlider("minY").setPosition(20, 80).setRange(0, 1)
				.setSize(guiWidth, 20);
		;
		cp5.addSlider("maxY").setPosition(20, 110).setRange(0, 1)
				.setSize(guiWidth, 20);
		;
		cp5.addSlider("minZ").setPosition(20, 140).setRange(0, 1)
				.setSize(guiWidth, 20);
		;
		cp5.addSlider("maxZ").setPosition(20, 170).setRange(0, 1)
				.setSize(guiWidth, 20);

		cp5.setAutoDraw(false);
	}

	public void gui() {
		noLights();
		stroke(0);
		fill(0);
		PGraphics3D p3d = ((PGraphics3D) g);
		PMatrix3D currCameraMatrix = new PMatrix3D(p3d.camera);
		camera();
		cp5.draw();
		p3d.camera = currCameraMatrix;
	}

	public void resetPreview() {
		dim = dimLow;
		iso = dim * 0.90f;
		dimHull = iso / dim;
		setBounds();
		createGrid();
		calculateDistancesSimple();

		findOutline(0, (int) (nY * 0.5f), (int) (nZ * 0.5f));

		println("start marchincube");
		// dualCube();
		meFaces = MCubeEngineLowMemory.initCubesTest(grid, iso, null);
		System.out.println("nFaces: " + meFaces.size());
		findBorders();
	}

	public void resetFine() {
		meFaces = null;
		dim = dimHigh;
		iso = dim * 1.5f;

		setBounds();
		createGrid();
		calculateDistances();

		//findOutline(0, (int) (nY * 0.5f), (int) (nZ * 0.5f));
		/*this.removeUnseen(0, (int) (nY * 0.5f), (int) (nZ * 0.5f));
		findOutline(0, (int) (nY * 0.5f), (int) (nZ * 0.5f));
		this.makeOffset();

		addBackParts();
		makeBorder();
		subHoles();*/
		println("start marchincube");
		PrintWriter output = createWriter(pathObjIn + "H" + minX + "_" + minY
				+ "_" + maxY + "_" + minZ + "inside" + dimHigh + "res005.obj");
		// dualCube();
		meFaces = MCubeEngineLowMemory.initCubesTest(grid, iso, null);
		
		saveSTL(this.selectOutput());
	//	output.flush(); // Writes the remaining data to the file
	//	output.close();
		println("saved");
	}

	public void saveSTL(String file) {
		STLWriter writer = new STLWriter();
		writer.beginSave(file + ".stl", meFaces.size());
		for (int i = 0; i < meFaces.size(); i++) {
			SimpleTriangle f = meFaces.get(i);
			writer.face(f.getNode(0), f.getNode(1), f.getNode(2));
		}
		writer.endSave();
	}

	public void saveDistance(String file) {
		try {
			FileOutputStream fileOutputStream = new FileOutputStream(file);
			PrintStream printstream = new PrintStream(fileOutputStream);
			printstream.println(grid.length + " " + grid[0].length + " "
					+ grid[0][0].length);
			printstream.println(b3d.x1 + " " + b3d.y1 + " " + b3d.z1 + " "
					+ b3d.x2 + " " + b3d.y2 + " " + b3d.z2);
			for (int x = 0; x < grid.length; x++) {
				println("x" + x + "/" + grid.length);
				for (int y = 0; y < grid[x].length; y++) {
					for (int z = 0; z < grid[x][y].length; z++) {
						Cell c = grid[x][y][z];
						printstream.println("" + c.d);
					}
				}
			}
			printstream.close();
			fileOutputStream.close();
		} catch (Exception e) {
			System.err.println("error writing file");
		}
	}

	public void loadData() {
		mesh.load(pathObjIn);
	}

	public void loadDistance(String file) {
		String[] lines = this.loadStrings(file);
		String cString = lines[0];
		String[] strings = cString.split(" ");
		int nX = Integer.valueOf(strings[0]);
		println("nX: " + nX);
		int nY = Integer.valueOf(strings[1]);
		int nZ = Integer.valueOf(strings[2]);

		String boundString = lines[1];
		strings = cString.split(" ");
		b3d = new Bounds3D();
		b3d.x1 = Float.valueOf(strings[0]);
		b3d.y1 = Float.valueOf(strings[1]);
		b3d.z1 = Float.valueOf(strings[2]);
		b3d.x2 = Float.valueOf(strings[3]);
		b3d.y2 = Float.valueOf(strings[4]);
		b3d.z2 = Float.valueOf(strings[5]);
		grid = new Cell[nX][nY][nZ];
		println(nY);
		int j = 0;
		for (int i = 1; i < lines.length; i++) {
			String l = lines[i];
			j = i - 1;
			int cX = ((int) (j * 1f / (nZ * nY)));
			int cY = (int) (j * 1f / nZ) % nY;
			int cZ = j % (nZ);
			double d = Double.valueOf(l);
			Cell cell = new Cell(cX, cY, cZ);
			cell.setValue(d);
			grid[cX][cY][cZ] = cell;
		}
	}

	public void findBorders() {
		borderFaces = new Vector<SimpleTriangle>();
		for (int i = 0; i < mesh.faces.size(); i++) {
			SimpleTriangle tri = (SimpleTriangle)mesh.faces.get(i);

			Box3D box = tri.getBounds3D();
			if (box.getX1() < b3d.getX1() && box.getX2() > b3d.getX1())
				borderFaces.add(tri);

			if (box.getX1() < b3d.getX2() && box.getX2() > b3d.getX2()) {
				borderFaces.add(tri);
			}
			if (box.getY1() < b3d.getY1() && box.getY2() > b3d.getY1()) {
				borderFaces.add(tri);
			}
			if (box.getY1() < b3d.getY2() && box.getY2() > b3d.getY2()) {
				borderFaces.add(tri);
			}
			if (box.getZ1() < b3d.getZ1() && box.getZ2() > b3d.getZ1()) {
				borderFaces.add(tri);
			}
			if (box.getZ1() < b3d.getZ2() && box.getZ2() > b3d.getZ2()) {
				borderFaces.add(tri);
			}
		}
	}

	public void dualCube() {
		// findOutline(0, (int) (nY * 0.5f), (int) (nZ * 0.5f));

		float[][][] values = new float[nX][nY][nZ];
		for (int x = 0; x < nX; x++) {
			for (int y = 0; y < nY; y++) {
				for (int z = 0; z < nZ; z++) {
					values[x][y][z] = grid[x][y][z].getValue();
				}
			}
		}
		DualCubeEngine dc = new DualCubeEngine();
		meFaces = dc.getFaces(values, iso);
		dualMesh = new BasicMesh();
		dualMesh.load(meFaces);
		dualMesh.scale(dim);
		println("faces: " + meFaces.size());
		dualMesh.translate(sX, sY, sZ);
		// this.saveObjPrintwriter(pathObjOut);
	}

	public void marchinCube() {
		findOutline(0, (int) (nY * 0.5f), (int) (nZ * 0.5f));
		println("start marchincube");
		PrintWriter output = createWriter(pathObjOut);

		meFaces = MCubeEngineLowMemory.initCubesTest(grid, iso, output);
		println("start save");

		output.flush(); // Writes the remaining data to the file
		output.close();
		println("saved");
		// this.saveObjPrintwriter(pathObjOut);
	}

	public void calculateDistancesSimple() {

		// Box3D box3D = b3d.getOffsetBox(dim*4f+iso);

		for (int i = 0; i < mesh.faces.size(); i++) {
			if (i % 1000000 == 0) {
				println(i + "/" + mesh.faces.size());
			}
			SimpleTriangle f = (SimpleTriangle) mesh.faces.get(i);

			Box3D faceBounds = f.getBounds3D();
			// faceBounds = faceBounds.getOffsetBox(iso);
			int x1 = xK(faceBounds.x1) - 1;
			int y1 = yK(faceBounds.y1) - 1;
			int z1 = zK(faceBounds.z1) - 1;
			x1 = max(x1, 0);
			y1 = max(y1, 0);
			z1 = max(z1, 0);
			int x2 = xK(faceBounds.x2) + 1;
			int y2 = yK(faceBounds.y2) + 1;
			int z2 = zK(faceBounds.z2) + 1;
			x2 = min(x2, nX);
			y2 = min(y2, nY);
			z2 = min(z2, nZ);

			HasPos3D v1 = f.getNode(0);
			HasPos3D v2 = f.getNode(1);
			HasPos3D v3 = f.getNode(2);
			Vec3D v12 = Vec3D.sub(v2, v1);
			Vec3D v13 = Vec3D.sub(v3, v1);
			Vec3D n = Vec3D.cross(v12, v13);
			n.normalize();
			float d0 = -n.dot(f.getNode(0));
			for (int x = x1; x < x2; x++) {
				for (int y = y1; y < y2; y++) {
					for (int z = z1; z < z2; z++) {
						float planeDist = n.dot(x * dim + sX, y * dim + sY, z
								* dim + sZ)
								+ d0;
						if (planeDist < iso) {
							Cell cell = grid[x][y][z];
							cell.setValue(0);
						}
					}
				}
			}
		}
	}

	public void calculateDistances() {

		// Box3D box3D = b3d.getOffsetBox(dim*4f+iso);
		TrianglePreCalc tri;
		for (int i = 0; i < mesh.faces.size(); i++) {
			if (i % 100000 == 0) {
				println(i + "/" + mesh.faces.size());
			}
			SimpleTriangle f = (SimpleTriangle) mesh.faces.get(i);
			 tri=new TrianglePreCalc(f.getNode(0),f.getNode(1),f.getNode(2));
			Box3D faceBounds = tri.getBounds3D();
			faceBounds = faceBounds.getOffsetBox(iso);
			int x1 = xK(faceBounds.x1) - 1;
			int y1 = yK(faceBounds.y1) - 1;
			int z1 = zK(faceBounds.z1) - 1;
			x1 = max(x1, 0);
			y1 = max(y1, 0);
			z1 = max(z1, 0);
			int x2 = xK(faceBounds.x2) + 2;
			int y2 = yK(faceBounds.y2) + 2;
			int z2 = zK(faceBounds.z2) + 2;
			x2 = min(x2, nX);
			y2 = min(y2, nY);
			z2 = min(z2, nZ);

			for (int x = x1; x < x2; x++) {
				for (int y = y1; y < y2; y++) {
					for (int z = z1; z < z2; z++) {
						// float minD = Float.MAX_VALUE;
						Cell cell = grid[x][y][z];
						float cellX = cell.getX();
						float cellY = cell.getY();
						float cellZ = cell.getZ();
						if (faceBounds.isOverlap(cellX - dim, cellY - dim,
								cellZ - dim, cellX + dim, cellY + dim, cellZ
										+ dim)) {
							float planeDist =tri.getDistancePlane(cellX, cellY, cellZ);
							if (planeDist < cell.getValue()) {
								float cDist = Float.MAX_VALUE;
								cDist=tri.getDistance(cellX, cellY, cellZ);
								
								if (cDist < cell.getValue()) {
									cell.setValue(cDist);
									cell.normal =tri.n;
								}
							}
						}
					}
				}
			}
		}
	}

	public void createGrid() {
		nX = (int) (b3d.getDimX() / dim) + 1;
		nY = (int) (b3d.getDimY() / dim) + 1;
		nZ = (int) (b3d.getDimZ() / dim) + 1;

		sX = b3d.getX1();
		sY = b3d.getY1();
		sZ = b3d.getZ1();
		grid = new Cell[nX][nY][nZ];
		println(nX + " " + nY + " " + nZ);

		for (int x = 0; x < nX; x++) {
			println("x: " + x + "/" + nX);
			for (int y = 0; y < nY; y++) {
				for (int z = 0; z < nZ; z++) {
					float x1 = x * dim + sX;
					float y1 = y * dim + sY;
					float z1 = z * dim + sZ;
					Cell cell = new Cell(x, y, z);

					cell.setValue(1000);
					grid[x][y][z] = cell;
				}
			}
		}
	}

	public void findOutline() {

		for (int y = 0; y < nY; y++) {
			for (int z = 0; z < nZ; z++) {
				boolean flag = false;
				for (int x = nX - 1; x >= 0; x--) {
					Cell nb = grid[x][y][z];

					if (nb.getValue() > iso) {
						nb.setValue(0);
					} else {
						flag = true;
						x = -1;
					}
				}
				if (!flag) {
					for (int x = nX - 1; x >= 0; x--) {
						Cell nb = grid[x][y][z];
						nb.setValue(1000);
					}
				}
			}
		}
		for (int y = 0; y < nY; y++) {
			for (int z = 0; z < nZ; z++) {
				Cell nb = grid[nX - 1][y][z];
				nb.setValue(1000);
			}
		}
		for (int x = 0; x < nX; x++) {
			for (int y = 0; y < nY; y++) {
				for (int z = 0; z < nZ; z++) {
					if (y < 20 || y > nY - 15) {
						Cell nb = grid[x][y][z];
						nb.setValue(1000);
					}
				}
			}
		}
	}

	public void findOutline(int cX, int cY, int cZ) {

		for (int x = 0; x < nX; x++) {
			for (int y = 0; y < nY; y++) {
				for (int z = 0; z < nZ; z++) {
					Cell cell = grid[x][y][z];
					cell.reached = false;
				}
			}
		}
		Vector<Cell> candidates = new Vector<Cell>();
		candidates.add(grid[cX][cY][cZ]);
		for (int i = 0; i < candidates.size(); i++) {
			Cell cell = candidates.get(i);
			cell.reached = true;
		}

		while (candidates.size() > 0) {
			Vector<Cell> nextCandidates = new Vector<Cell>();
			for (int i = 0; i < candidates.size(); i++) {
				Cell cell = candidates.get(i);

				int sX = max(cell.getXK() - 1, 0);
				int sY = max(cell.getYK() - 1, 0);
				int sZ = max(cell.getZK() - 1, 0);

				int eX = min(cell.getXK() + 2, nX);
				int eY = min(cell.getYK() + 2, nY);
				int eZ = min(cell.getZK() + 2, nZ);

				for (int x = sX; x < eX; x++) {
					for (int y = sY; y < eY; y++) {
						for (int z = sZ; z < eZ; z++) {
							Cell nb = grid[x][y][z];
							if (!nb.reached && nb.getValue() > iso) {
								nb.reached = true;
								nextCandidates.add(nb);
							}
						}
					}
				}
			}
			candidates = nextCandidates;
		}
		for (int x = 0; x < nX; x++) {
			for (int y = 0; y < nY; y++) {
				for (int z = 0; z < nZ; z++) {
					Cell cell = grid[x][y][z];
					if (!cell.reached && cell.getValue() > iso) {
						cell.setValue(0);// 0
					}
				}
			}
		}
	}
	int maxXGrid;
	float oBackZ=0.16f;// distance horizontal back
	float oBackY=0.16f;// distance vertical back
	float dimIron=0.04f;
	
	public void addBackParts() {
		float thickness = 0.08f;// thickness of the backSides
		int nT=(int)(thickness/(dim*2))+1;
		Bounds3D b3d=getSolidBounds();
		int x1=(int)Math.max(b3d.x1,0);
		int y1=(int)Math.max(b3d.y1,0);
		int z1=(int)Math.max(b3d.z1,0);
		int x2=(int)Math.min(b3d.x2,nX);
		int y2=(int)Math.min(b3d.y2,nY);
		int z2=(int)Math.min(b3d.z2,nZ);
		
		int maxXGrid =(int) b3d.getX2();

		
		int nOZ=(int)(oBackZ/dim)+1;
		int nOY=(int)(oBackY/dim)+1;
		
		int nDim = (int) (thickness / dim);
		addBackPart(maxXGrid, 0, 0, nY, nDim);// left
		addBackPart(maxXGrid, 0, nZ - nDim, nY, nZ);// right
		
		int centerZ=(int) (nZ*0.5f);
		
		addBackPart(maxXGrid, 0,z1+ nOZ-nT, nY,z1+ nOZ+nT);// leftVer
		addBackPart(maxXGrid, 0, z2-nOZ-nT-1, nY, z2-nOZ+nT-1);//rightVer
		
		addBackPart(maxXGrid, y1+nOY-nT, 0, y1+nOY+nT, nZ);// leftVer
		addBackPart(maxXGrid, y2-nOY-nT-1, 0, y2-nOY+nT-1, nZ);//rightVer
		
		addBackPart(maxXGrid, nY - nDim, 0, nY, nZ);// top
		addBackPart(maxXGrid, 0, 0, nDim, nZ);// down
	}

	public void addBackPart(int xStart, int y1, int z1, int y2, int z2) {
		for (int y = y1; y < y2; y++) {
			for (int z = z1; z < z2; z++) {
				int cX = xStart;
				Cell cell = grid[cX][y][z];
				while (cell.d > iso && cX > 0 && !cell.reached) {
					cell.setValue(0);
					cX--;
					cell = grid[cX][y][z];
				}
			}
		}
	}
	public Bounds3D getSolidBounds(){
		Bounds3D b3d=new Bounds3D();
		for (int x = 0; x < nX; x++) {
			for (int y = 0; y < nY; y++) {
				for (int z = 0; z < nZ; z++) {
					Cell cell = grid[x][y][z];
					if (cell.getValue()<iso){
						b3d.addPoint(cell.x,cell.y,cell.z);
					}
				}
			}
		}
		return b3d;
	}
	
	
	public void subHoles() {
		Bounds3D b3d=getSolidBounds();
		println(b3d);
		int x1=(int)Math.max(b3d.x1,0);
		int y1=(int)Math.max(b3d.y1,0);
		int z1=(int)Math.max(b3d.z1,0);
		int x2=(int)Math.min(b3d.x2,nX);
		int y2=(int)Math.min(b3d.y2,nY);
		int z2=(int)Math.min(b3d.z2,nZ);
		
		int nT=(int)(dimIron/(dim*2f))+1;
		int nOX=(int)(0.08f/dim)+1;
		int nOY=(int)(oBackY/dim)+1;
		int nOZ=(int)(oBackZ/dim)+1;
		int xS=x2-nOX;
		int yS=y2-nOY;
		subHole(xS-nT,yS-nT,0,xS+nT,yS+nT,nZ);
		yS=y1+nOY;
		subHole(xS-nT,yS-nT,0,xS+nT,yS+nT,nZ);
		
		int zS=z1+nOZ;
		subHole(xS-nT,0,zS-nT,xS+nT,nY,zS+nT);
		zS=z2-nOZ;
		subHole(xS-nT,0,zS-nT,xS+nT,nY,zS+nT);
	}
	public void subHole(int x1, int y1, int z1, int x2, int y2, int z2) {
		println(x1+" "+y1+" "+z1+" "+x2+" "+y2+" "+z2);
		int nFull=0;
		for (int x = x1; x < x2; x++) {
			for (int y = y1; y < y2; y++) {
				for (int z = z1; z < z2; z++) {
					Cell cell = grid[x][y][z];
					
					cell.setValue(1000);
					nFull++;
				}
			}
		}
		println("nFull: "+nFull);
	}

	public void removeUnseen(int cX, int cY, int cZ) {
		Vector<Cell> candidates = new Vector<Cell>();
		float iso1 = 0.03f;
		float iso2 = 0.1f;

		float[][][] values = new float[nX][nY][nZ];
		for (int x = 0; x < nX; x++) {
			for (int y = 0; y < nY; y++) {
				for (int z = 0; z < nZ; z++) {
					Cell cell = grid[x][y][z];
					if (!cell.reached) {
						values[x][y][z] = 0;
					} else {
						values[x][y][z] = 10000;
					}
				}
			}
		}
		DepthAnalyser3D dA = new DepthAnalyser3D();
		dA.calculateDepth(values, dim * 1f, dim * 1.414222f, dim * 1.7321f);
		for (int x = 0; x < nX; x++) {
			for (int y = 0; y < nY; y++) {
				for (int z = 0; z < nZ; z++) {
					values[x][y][z] = 10000;
				}
			}
		}

		candidates.add(grid[cX][cY][cZ]);
		for (int i = 0; i < candidates.size(); i++) {
			Cell cell = candidates.get(i);
			values[cell.x][cell.y][cell.z] = 0;
		}
		while (candidates.size() > 0) {
			Vector<Cell> nextCandidates = new Vector<Cell>();
			for (int i = 0; i < candidates.size(); i++) {
				Cell cell = candidates.get(i);

				int sX = max(cell.getXK() - 1, 0);
				int sY = max(cell.getYK() - 1, 0);
				int sZ = max(cell.getZK() - 1, 0);

				int eX = min(cell.getXK() + 2, nX);
				int eY = min(cell.getYK() + 2, nY);
				int eZ = min(cell.getZK() + 2, nZ);

				for (int x = sX; x < eX; x++) {
					for (int y = sY; y < eY; y++) {
						for (int z = sZ; z < eZ; z++) {
							Cell nb = grid[x][y][z];
							if (values[nb.x][nb.y][nb.z] == 10000
									&& dA.depth[x][y][z] > iso1) {
								values[nb.x][nb.y][nb.z] = 0;
								nextCandidates.add(nb);
							}
						}
					}
				}
			}
			candidates = nextCandidates;
		}

		dA = new DepthAnalyser3D();
		dA.calculateDepth(values, dim * 1f, dim * 1.414222f, dim * 1.7321f);
		values = dA.depth;
		int iForFromSurface = 0;
		for (int x = 0; x < nX; x++) {
			for (int y = 0; y < nY; y++) {
				for (int z = 0; z < nZ; z++) {
					float d = values[x][y][z];
					if (d > iso2) {
						Cell cell = grid[x][y][z];
						cell.reached = false;
						cell.setValue(0);
					}
				}
			}
		}
	}

	public void makeBorder() {
		for (int x = 0; x < nX; x++) {
			for (int y = 0; y < nY; y++) {
				for (int z = 0; z < nZ; z++) {
					Cell cell = grid[x][y][z];
					if (!cell.reached
							&& (x == 0 || x == nX - 1 || z == 0 || z == nZ - 1
									|| y == 0 || y == nY - 1)) {
						cell.setValue(1000);
					}
				}
			}
		}
	}

	public void makeOffset() {
		float[][][] values = new float[nX][nY][nZ];
		for (int x = 0; x < nX; x++) {
			for (int y = 0; y < nY; y++) {
				for (int z = 0; z < nZ; z++) {
					Cell cell = grid[x][y][z];
					values[x][y][z] = cell.getValue();
					if (!cell.reached) {// nicht drin=hoher abstand
						values[x][y][z] = 10000;
					} else {
						if (cell.getValue() < iso + dim) {
							values[x][y][z] = -cell.getValue();// ist drin also
																// gleich 0
						} else {
							values[x][y][z] = -(iso + dim);
						}
					}
				}
			}
		}

		DepthAnalyser3D dA = new DepthAnalyser3D();
		dA.calculateDepth(values, dim * 1f * c, dim * 1.414222f * c, dim
				* 1.7321f * c);

		values = dA.depth;
		for (int x = 0; x < nX; x++) {
			for (int y = 0; y < nY; y++) {
				for (int z = 0; z < nZ; z++) {
					Cell cell = grid[x][y][z];
					if (!cell.reached && values[x][y][z] > iso) {
						cell.setValue(values[x][y][z]);
					} else {
						// cell.setValue(0);
					}
				}
			}
		}

	}

	public int xK(float x) {
		return (int) ((x - sX) / dim);
	}

	public int yK(float y) {
		return (int) ((y - sY) / dim);
	}

	public int zK(float z) {
		return (int) ((z - sZ) / dim);
	}

	public void setBounds() {
		realBounds = mesh.getBounds3D();

		b3d.x1 = realBounds.x1 + minX * realBounds.getDimX();
		b3d.x2 = realBounds.x2 - maxX * realBounds.getDimX(); // 0.05F
		b3d.z1 = realBounds.z1 + minZ * realBounds.getDimZ();
		b3d.z2 = realBounds.z2 - maxZ * realBounds.getDimZ();
		b3d.y1 = realBounds.y1 + minY * realBounds.getDimY();
		b3d.y2 = realBounds.y2 - maxY * realBounds.getDimY();
		b3d.reOrient();

		// mesh.scale(scaleX, scaleY, scaleZ);
		// b3d=mesh.getBounds3D();
	}

	public void draw() {
		// setBounds();
		if (flagLoadDistance) {
			this.loadDistance(this.selectInput());
		}
		if (flagResetPreview) {
			resetPreview();
			flagResetPreview = false;
		}
		if (flagResetFine) {
			resetFine();
			flagResetFine = false;
		}
		if (flagDistance) {
			createGrid();
			calculateDistances();
			flagDistance = false;
		}

		if (flagMarching) {
			dualCube();
			flagMarching = false;
		}

		if (flagSave) {
			if (dualMesh != null) {
				dualMesh.saveObj(this.selectOutput());
				println("saved");
				flagSave = false;
			}
		}
		setBounds();
		if (cp5.window().isMouseOver()) {
			cam.setActive(false);
		} else {
			cam.setActive(true);
		}
		display();
		gui();
	}

	public void display() {
		hint(ENABLE_DEPTH_TEST);
		pushMatrix();
		lights();
		rectMode(CENTER);
		background(0);
		scale(200, 200, 200);
		float tol = 0.03f;
		noFill();
		stroke(0, 0, 255);
		realBounds.display3D(g);
		stroke(255, 0, 0);
		b3d.display3D(g);
		if (!mousePressed && mouseX > guiWidth) {
			noStroke();
			fill(255);
			if (meFaces != null) {
				for (int i = 0; i < meFaces.size(); i++) {
					SimpleTriangle f = meFaces.get(i);
					f.display3D(g);
				}
			}

			if (dualMesh != null) {
				dualMesh.display3D(g);
			}
			fill(255, 0, 0);
			stroke(255, 0, 0);
			strokeWeight(2f);
			for (int i = 0; i < borderFaces.size(); i++) {
				SimpleTriangle tri = borderFaces.get(i);
				tri.display3D(g);
			}
		}
		popMatrix();
		hint(DISABLE_DEPTH_TEST);
	}

	public void keyPressed() {
		if (key == 'd') {
			flagDistance = true;
			flagMarching = true;
		}
		if (key == 'f') {
			// flagMarching = true;
		}
		if (key == 'r') {
			flagResetPreview = true;
		}
		if (key == 't') {
			flagResetFine = true;
		}
		if (key == 's') {
			flagSave = true;
		}
		if (key == 'l') {
			flagLoadDistance = true;
		}
		flagRefresh = true;

	}

	public class Cell implements IsMCVertex {

		short x, y, z;
		float d = Float.MAX_VALUE;
		Vec3D normal;
		boolean reached;
		MCube cube;

		public Cell(int x, int y, int z) {
			this.x = (short) x;
			this.y = (short) y;
			this.z = (short) z;
		}

		public float getValue() {
			// TODO Auto-generated method stub
			return d;
		}

		public void setValue(double value) {
			// TODO Auto-generated method stub
			this.d = (float) value;
		}

		public MCube getCube() {
			// TODO Auto-generated method stub
			return cube;
		}

		@Override
		public float getX() {
			// TODO Auto-generated method stub
			// return nX%3;
			return x * dim + sX;
			// return x;
		}

		@Override
		public float getY() {
			// TODO Auto-generated method stub
			return y * dim + sY;
			// return y;
		}

		public float getZ() {
			// TODO Auto-generated method stub
			return z * dim + sZ;
			// return z;
		}

		public int getXK() {
			// TODO Auto-generated method stub
			// return nX%3;
			return x;
			// return x;
		}

		public int getYK() {
			// TODO Auto-generated method stub
			return y;
			// return y;
		}

		public int getZK() {
			// TODO Auto-generated method stub
			return z;
			// return z;
		}

		public void setCube(MCube cube) {
			// TODO Auto-generated method stub
			this.cube = cube;
		}

		@Override
		public void setPosition(float x, float y, float z) {
			// TODO Auto-generated method stub

		}

		@Override
		public void setZ(float z) {
			// TODO Auto-generated method stub
			// this.z=z;
		}

		@Override
		public void setPosition(float x, float y) {
			// TODO Auto-generated method stub

		}

		@Override
		public void setX(float x) {
			// TODO Auto-generated method stub

		}

		@Override
		public void setY(float Y) {
			// TODO Auto-generated method stub

		}

		@Override
		public int getId() {
			// TODO Auto-generated method stub
			return 0;
		}

		@Override
		public void setId(int i) {
			// TODO Auto-generated method stub

		}
	}
}
